﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SmartMathLibrary
{
    /// <summary>
    /// This class represents a part or a component of a cubic spline.
    /// </summary>
    [Serializable]
    public class CubicSplineComponent : AbstractSplineComponent, ICubicSplineComponent
    {
        /// <summary>
        /// The polynomial, which defines the splinepart.
        /// </summary>
        private SimplePolynomial polynomial;

        /// <summary>
        /// Initializes a new instance of the <see cref="CubicSplineComponent"/> class.
        /// </summary>
        /// <param name="a">The constant value of the polynomial x^0.</param>
        /// <param name="b">The x^1 value of the polynomial.</param>
        /// <param name="c">The x^2 value of the polynomial.</param>
        /// <param name="d">The x^3 value of the polynomial.</param>
        /// <param name="startX">The startvalue of the domain for this splinepart.</param>
        /// <param name="endX">The end value of the domain for this splinepart.</param>
        public CubicSplineComponent(double a, double b, double c, double d, double startX, double endX)
        {
            this.polynomial = new SimplePolynomial(new double[] { a, b, c, d });

            if (endX < startX)
            {
                this.start = new Point2D(endX, this.polynomial.SolveAt(endX));
                this.end = new Point2D(startX, this.polynomial.SolveAt(startX));
            }
            else
            {
                this.start = new Point2D(startX, this.polynomial.SolveAt(startX));
                this.end = new Point2D(endX, this.polynomial.SolveAt(endX));
            }
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="CubicSplineComponent"/> class.
        /// </summary>
        /// <param name="a">The constant value of the polynomial x^0.</param>
        /// <param name="b">The x^1 value of the polynomial.</param>
        /// <param name="c">The x^2 value of the polynomial.</param>
        /// <param name="d">The x^3 value of the polynomial.</param>
        /// <param name="start">The start point of the domain for this splinepart.</param>
        /// <param name="end">The endpoint of the domain for this splinepart.</param>
        public CubicSplineComponent(double a, double b, double c, double d, Point2D start, Point2D end)
        {
            if (start == (Point2D) null)
            {
                throw new ArgumentNullException("start");
            }

            if (end == (Point2D) null)
            {
                throw new ArgumentNullException("end");
            }

            if (end.X < start.X)
            {
                this.start = end;
                this.end = start;
            }
            else
            {
                this.start = start;
                this.end = end;
            }

            this.polynomial = new SimplePolynomial(new double[] {a, b, c, d});
        }

        /// <summary>
        /// Gets or sets the polynomial of the splinepart.
        /// </summary>
        /// <value>The polynomial of the splinepart.</value>
        public SimplePolynomial Polynomial
        {
            get { return polynomial; }
            set { polynomial = value; }
        }

        /// <summary>
        /// Copies the current instance of a CubicSplineComponent.
        /// </summary>
        /// <returns>A copy of the current instance.</returns>
        public CubicSplineComponent Copy()
        {
            return new CubicSplineComponent(this.polynomial.Coefficients[0],
                                            this.polynomial.Coefficients[1],
                                            this.polynomial.Coefficients[2],
                                            this.polynomial.Coefficients[3], this.start, this.end);
        }

        /// <summary>
        /// Determines whether [the current CubicSplineComponent is defined for] [the specified x value].
        /// </summary>
        /// <param name="x">The x value to check.</param>
        /// <returns>
        /// 	<c>true</c> if [the current CubicSplineComponent is defined for] [the specified x value]; otherwise, <c>false</c>.
        /// </returns>
        public bool IsDefinedFor(double x)
        {
            return ((this.start.X <= x) && (x <= this.end.X));
        }

        /// <summary>
        /// Compares the current instance to another CubicSplineComponent.
        /// </summary>
        /// <param name="compare">The CubicSplineComponent to compare.</param>
        /// <returns>True if the components are even otherwise, false.</returns>
        public bool CompareTo(CubicSplineComponent compare)
        {
            if (compare == (CubicSplineComponent) null)
            {
                throw new ArgumentNullException("compare");
            }

            return ((this.polynomial == compare.Polynomial) && (this.start == compare.Start)
                    && (this.end == compare.End));
        }
    }
}